/**
 * SVG Tools
 */

var Tools = Tools || {};
var Plot = Plot || {};

Tools.createSVGNode = function (type, args) {
    'use strict';
    type = type.toLowerCase();
    var element = document.createElementNS('http://www.w3.org/2000/svg', type);
    var i;
    for (i in args) {
        if (args.hasOwnProperty(i)) {
            element.setAttributeNS(null, i, args[i]);
        }
    }
    return element;
};
Tools.createSVGTextNode = function (text, args) {
    'use strict';
    var element = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    var i;
    for (i in args) {
        if (args.hasOwnProperty(i)) {
            element.setAttributeNS(null, i, args[i]);
        }
    }
    element.appendChild(document.createTextNode(text));
    return element;
};

Tools.getPosition = function (e, event) {
    'use strict';
    var left = 0;
    var top = 0;

    // Tant que l'on a un élément parent
    while (e.offsetParent !== undefined && e.offsetParent !== null) {
	// On ajoute la position de l'élément parent
        left += e.offsetLeft + (e.clientLeft !== null ? e.clientLeft : 0);
        top += e.offsetTop + (e.clientTop !== null ? e.clientTop : 0);
        e = e.offsetParent;
    }

    left = -left + event.pageX;
    top = -top + event.pageY;

    return [left, top];
};

Tools.isArrayLike = function (obj) {
    'use strict';
    return obj && typeof obj === 'object' && obj.length !== undefined;
};

Tools.Vector = function (arg1, arg2, arg3, arg4) {
    'use strict';
    var n, b, s, e, i, a, x;
    if (Tools.isArrayLike(arg1)) {
        n = arg1.length;
        this.data = this.zeros(n, arg4);
        this.index = this.zeros(n, Uint32Array);
        for (i = 0; i < n; i++) {
            this.data[i] = arg1[i];
            this.index[i] = i;
        }
    } else if (arg1 instanceof Tools.Vector) {
        n = arg1.index.length;
        this.data = this.zeros(n);
        this.index = this.zeros(n, Uint32Array);
        i = this.index;
        var d = this.data;
        var ii = arg1.index;
        var id = arg1.data;
        for (x = 0; x < n; x++) {
            d[x] = id[ii[x]];
            i[x] = x;
        }
    } else {
        if (arg1 !== undefined && arg2 !== undefined && arg3 !== undefined) {
            b = arg1;
            s = arg2;
            e = arg3;
        } else if (arg1 !== undefined && arg2 !== undefined && arg3 === undefined) {
            b = arg1;
            s = arg1 < arg2 ? 1 : -1;
            e = arg2;
        } else if (arg1 !== undefined && arg2 === undefined && arg3 === undefined) {
            b = 0;
            s = 0;
            e = arg1;
        }
        if (s !== 0) {
            n = Math.abs(Math.floor((e - b) / s) + 1);
        } else {
            n = Math.abs(Math.floor((e - b)));
        }
        this.data = this.zeros(n, arg4);
        this.index = this.zeros(n, Uint32Array);
        a = b;
        for (i = 0; i < n; i++, a += s) {
            this.data[i] = a.toPrecision(15);
            this.index[i] = i;
        }
    }
    return this;
};

Tools.Vector.prototype.zeros = function (n, Type) {
    'use strict';
    var out;
    if (!Type) {
        out = new Float64Array(n);
    } else {
        out = new Type(n);
    }
    return out;
};

Tools.Vector.linearSpace = function (b, e, n) {
    'use strict';
    var n1 = Math.floor(n) - 1;
    var c = (e - b) * (n - 2);
    var out = new Tools.Vector(b, (e - b) / n1, e);
    out.data[n - 1] = e;
    return out;
};

Tools.Vector.prototype.derive = function (o) {
    'use strict';
    o = o || 1;
    var d, i, x, n;
    d = this.data;
    i = this.index;
    n = i.length;
    for (x = 1; x < n; x++) {
        d[i[x - 1]] = d[i[x]] - d[i[x - 1]];
    }
    this.data = this.data.subarray(0, n - 1);
    this.index = this.index.subarray(0, n - 1);
    if (o > 1) {
        this.derive(o - 1);
    }
    return this;
};


Tools.Vector.prototype.max = function () {
    'use strict';
    var d, n, x, i, nx;
    d = this.data;
    i = this.index;
    nx = i.length;
    var M = -Infinity;
    var Mind = NaN;
    for (x = 0; x < nx; x++) {
        if (d[i[x]] > M) {
            M = d[i[x]];
            Mind = i[x];
        }
    }
    return new Tools.Vector([M, Mind]);
};

Tools.Vector.prototype.min = function () {
    'use strict';
    var d, n, x, i, nx;
    d = this.data;
    i = this.index;
    nx = i.length;
    var M = +Infinity;
    var Mind = NaN;
    for (x = 0; x < nx; x++) {
        if (d[i[x]] < M) {
            M = d[i[x]];
            Mind = i[x];
        }
    }
    return new Tools.Vector([M, Mind]);
};

Tools.Vector.prototype.get = function (i) {
    'use strict';
    return this.data[this.index[i]];
};

Tools.Vector.prototype.set = function (i, v) {
    'use strict';
    this.data[this.index[i]] = v;
    return this;
};

/** Make a HTML element draggable: objects can be dropped on it.
 * @memberOf Tools
 * @param {HTMLElement|String} element
 *  HTMLElement or HTMLElement id wich is desired to be draggable.
 * @param {function} callback
 *  Function to be called when files will be drag on element.
 * @param {string} [type='none']
 *  Specify the way of reading the file.<br />
 *  Can be 'DataUrl | url', 'ArrayBuffer | bin | binary', or 'text'.
 * @example
 *  // Drag callback: load the image
 *  var main = function(result) {
 *      // Load callback: display the image
 *      var callback = function(im) {
 *          im.draw(createView(1, 1));
 *      };
 *      // Load the image
 *      var im = new ImageJS().load(result, callback);
 *  };
 *  // Make the canvas with id 'canvas' draggable
 *  Tools.makeDraggable('canvasId', main);
 */
Tools.makeDraggable = function (element, callback, type) {
    "use strict";

    // Deal with arguments
    type = (type || 'none').toLowerCase();
    switch (type) {
    case 'dataurl':
    case 'url':
        type = 'url';
        break;
    case 'text':
    case 'txt':
        type = 'txt';
        break;
    case 'arraybuffer':
    case 'binary':
    case 'bin':
        type = 'bin';
        break;
    default:
        type = 'none';
    }

    if (typeof element === 'string') {
        element = document.getElementById(element) || element;
    }

    // Callback functions declarations
    var dragEnter, dragLeave, dragOver;
    dragEnter = dragLeave = dragOver = function (evt) {
        evt.stopPropagation();
        evt.preventDefault();
    };

    var drop = function (evt) {
        evt.stopPropagation();
        evt.preventDefault();

        // File handling functions
        var handleFile, newCallback;
        if (type !== 'none') {
            newCallback = function (evt) {
                if (callback) {
                    callback(evt.target.result, evt);
                }
            };
            handleFile =  function (file) {
                var reader = new FileReader();
                reader.onload = newCallback;
                switch (type) {
                case 'url':
                    reader.readAsDataURL(file);
                    break;
                case 'txt':
                    reader.readAsText(file);
                    break;
                case 'bin':
                    reader.readAsArrayBuffer(file);
                    break;
                }
            };
        } else {
            handleFile = function (file) {
                if (callback) {
                    callback(file);
                }
            };
        }

        // Only call the handler if 1 or more files was dropped.
        if (evt.dataTransfer.files.length) {
            var i;
            for (i = 0; i < evt.dataTransfer.files.length; i++) {
                handleFile(evt.dataTransfer.files[i]);
            }
        }
    };

    // Drag Drop on HTML element.
    element.addEventListener('dragenter', dragEnter, false);
    element.addEventListener('dragleave', dragLeave, false);
    element.addEventListener('dragover', dragOver, false);
    element.addEventListener('drop', drop, false);
};

Tools.parseCsv = function (csv, vDelim, hDelim, transpose) {
    'use strict';
    transpose = transpose || false;
    /*
    var tabRow = csv.split(vDelim);
    var output = [];
    var i;
    for (i = 0; i < tabRow.length; i++) {
        if (/\d/.test(tabRow[i])) {
            output.push(tabRow[i].split(hDelim));
        }
    }
    */
    vDelim = vDelim || "\n";
    var csv = csv.split(vDelim);
    if (csv[csv.length - 1] === "") {
        csv.pop();
    }
    var i, ei;
    var output = [];

    for (i = 0, ei = csv.length; i < ei; i++) {
        output.push(csv[i].match(/[\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?/g));
    }
    return transpose ? Tools.Array.transpose(output) : output;
};

Tools.Array = Tools.Array || {};
Tools.Array.opposite = function (a) {
    'use strict';
    var i;
    for (i = 0; i < a.length; i++) {
        if (a[i] instanceof Array) {
            Tools.Array.opposite(a[i]);
        } else {
            a[i] = -a[i];
        }
    }
    return a;
};

Plot.prototype.appendTo = function (parent) {
    'use strict';
    if (typeof parent === 'string') {
        parent = document.getElementById(parent);
    } else {
        parent = parent || document.body;
    }
    parent.appendChild(this.getDrawing());
    this.autoDisplay();
    return this;
};

/** Apply a function to each value of an array or an array of arrays.
 * @param {Array} array
 * @param {function} f
 *  Function to be applied to each element of the array.
 * @return {Array}
 *  Array of f(t) for all t in the input array.
 */
Tools.Array.mapRec = function (a, f) {
    'use strict';
    var i, N = a.length;
    var aOut = new a.constructor(N);
    for (i = 0; i < N; i++) {
        if (a[i].length !== undefined && typeof a[i] !== 'string') {
            aOut[i] = Tools.Array.mapRec(a[i], f);
        } else {
            aOut[i] = f(a[i]);
        }
    }
    return aOut;
};


/** Is a 2D array rectangular?
 * @param {Array} array
 *  An array of arrays.
 * @return {boolean}
 *  True iff all the sub-arrays have the same length.
 */
Tools.Array.isRectangle = function (a) {
    'use strict';
    if (!a || !a.length || a[0].length === undefined) {
        return false;
    }
    var i, N = a.length;
    var P = a[0].length;
    for (i = 1; i < N; i++) {
        if (a[i].length !== P) {
            return false;
        }
    }
    return true;
};

/** Transpose an array of arrays.
 * @param {Array} a
 *  Array to be transposed.
 * @return {Array}
 *  Transposed array.
 */
Tools.Array.transpose = function (a) {
    'use strict';
    var errMsg = 'Tools.Array.transpose: ';
    if (!Tools.Array.isRectangle(a)) {
        throw new Error(errMsg + 'cannot transpose a non-rectangular array');
    }
    var i, N = a.length;
    var j, P = a[0].length;
    var aOut = new a.constructor(P);
    for (j = 0; j < P; j++) {
        aOut[j] = new a[0].constructor(N);
        for (i = 0; i < N; i++) {
            aOut[j][i] = a[i][j];
        }
    }
    return aOut;
};
